﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using System.Diagnostics;
using SimpleIR;

namespace InterpreterCore
{
	/// <summary>
	/// This class is a base class for both sted heap allocated objects and arrays.
	/// </summary>
	internal class CObject : CLocation
	{
		/// <summary>
		/// The layout of this object.
		/// </summary>
		protected CompositeValueEntry[] m_objcontents;

		internal CObject(int address, IRClassType loctype, CompositeValueEntry[] contents)
			: base(address, loctype)
		{
			Debug.Assert(address >= 0);

			IRSignedIntegerType i32 = GlobalInterpreterState.TProg.TypeTable.Int32Type;

			this.m_objcontents = contents; 

			IRMemberField hashfield = GlobalInterpreterState.TProg.TypeTable.RootObjectHashField;
			IRBasicAccessPath baph = GlobalInterpreterState.TProg.GetAccessPathForSingleField(hashfield);
			CompositeValue.StorePassableNumericValTo(NumericValue.MakeSignedValueOfType(address, i32), baph, this.m_objcontents);

			IRMemberField rttifield = GlobalInterpreterState.TProg.TypeTable.RootObjectRTTIField;
			IRBasicAccessPath baprtti = GlobalInterpreterState.TProg.GetAccessPathForSingleField(rttifield);
			CompositeValue.StorePassableNumericValTo(NumericValue.MakeSignedValueOfType(loctype.TypeID, i32), baprtti, this.m_objcontents);
		}

		internal static CObject InterpAllocateObject(int address, IRClassType loctype)
		{
			CompositeValueEntry[] cvv = CompositeValue.MakeCompositeValue(loctype);

			return new CObject(address, loctype, cvv);
		}

		internal void MarkRTTIUsed()
		{
			IRBasicAccessPath rttip = GlobalInterpreterState.TProg.GetAccessPathForSingleField(GlobalInterpreterState.TProg.TypeTable.RootObjectRTTIField);
			TracingSystem.LogUseFromLocation(this.Address, rttip, this.LocationType);
		}

		public override string ToString()
		{ return String.Format("{0}:{1}", this.LocationType.BaseTypeName, CompositeValue.Stringify(this.m_objcontents)); }

		internal override string DebuggerString()
		{ return String.Format("{1} at {0} : {2}", this.Address, this.LocationType.BaseTypeName, CompositeValue.DebuggerString(this.m_objcontents)); }

		internal override void SerializeForHeapDump(System.IO.TextWriter dumpstr)
		{
			string ohs = String.Format("LFlag=Obj LAddr={0} LType={1} LValue=", this.Address, this.LocationType.TypeID);
			dumpstr.Write(ohs);
			CompositeValue.SerializeForHeapDump(dumpstr, this.m_objcontents);
		}

		internal override void SerializeForDGMLDisplay(System.Xml.XmlWriter nodexw, System.Xml.XmlWriter edgexw, string lname)
		{
			nodexw.WriteStartElement("Node");
			nodexw.WriteAttributeString("Id", this.Address.ToString());
			nodexw.WriteAttributeString("Label", this.LocationType.BaseTypeName);
			nodexw.WriteAttributeString("Background", "grey");
			nodexw.WriteEndElement();

			for(int i = 0; i < this.m_objcontents.Length; ++i)
			{
				if(this.m_objcontents[i].Offset.PathLast.FieldType is IRPtrType)
				{
					PtrValue pv = this.m_objcontents[i].Value.ConvertToPassablePtrValue();
					if(!pv.IsNullPtr)
					{
						edgexw.WriteStartElement("Link");
						edgexw.WriteAttributeString("Source", this.Address.ToString());
						edgexw.WriteAttributeString("Target", pv.TargetAddress.ToString());
						edgexw.WriteAttributeString("Label", this.m_objcontents[i].Offset.FieldOnlyDisplayString());
						edgexw.WriteEndElement();
					}
				}
			}
		}

		internal static CObject ObjectParseLocation(Dictionary<string, string> pkvp)
		{
			Debug.Assert(pkvp["LFlag"].Equals("Obj"));

			int addr = Int32.Parse(pkvp["LAddr"]);

			int typeid = Int32.Parse(pkvp["LType"]);
			IRClassType ltype = (IRClassType)GlobalInterpreterState.TProg.TypeTable.LookupTypeForID(typeid);

			CompositeValueEntry[] cve = CompositeValue.ParseValue(ltype, pkvp["LValue"]);

			return new CObject(addr, ltype, cve);
		}

		//////////////////////////////////////////////////////
		//Object specific field accesses

		internal PtrValue ReadPassablePtrValueFromField(IRMemberField f)
		{
			Debug.Assert(f.FieldType is IRPtrType);

			IRBasicAccessPath bap = GlobalInterpreterState.TProg.GetAccessPathForSingleField(f);
			TracingSystem.LogUseFromLocation(this.Address, bap, this.LocationType);

			return CompositeValue.ReadPassablePtrValueFrom(bap, this.m_objcontents);
		}

		internal void StorePassablePtrValToField(PtrValue pv, IRMemberField f)
		{
			Debug.Assert(f.FieldType is IRPtrType);

			IRBasicAccessPath bap = GlobalInterpreterState.TProg.GetAccessPathForSingleField(f);
			TracingSystem.LogOldPointer(this.Address, bap, CompositeValue.ReadPassablePtrValueFrom(bap, this.m_objcontents));
			TracingSystem.LogModToLocation(this.Address, bap, this.LocationType);
			
			CompositeValue.StorePassablePtrValTo(pv, bap, this.m_objcontents);
		}

		internal NumericValue ReadPassableNumericFromField(IRMemberField f)
		{
			Debug.Assert(f.FieldType is IRPrimitiveNumericType || f.FieldType is IREnumType);

			IRBasicAccessPath bap = GlobalInterpreterState.TProg.GetAccessPathForSingleField(f);
			TracingSystem.LogUseFromLocation(this.Address, bap, this.LocationType);

			return CompositeValue.ReadPassableNumericFrom(bap, this.m_objcontents);
		}

		internal void StorePassableNumericValToField(NumericValue nv, IRMemberField f)
		{
			Debug.Assert(f.FieldType is IRPrimitiveNumericType || f.FieldType is IREnumType);

			IRBasicAccessPath bap = GlobalInterpreterState.TProg.GetAccessPathForSingleField(f);
			TracingSystem.LogOldNumeric(this.Address, bap, CompositeValue.ReadPassableNumericFrom(bap, this.m_objcontents));
			TracingSystem.LogModToLocation(this.Address, bap, this.LocationType);

			CompositeValue.StorePassableNumericValTo(nv, bap, this.m_objcontents);
		}

		internal CompositeValueEntry[] ReadCompositeFromField(IRMemberField f)
		{
			Debug.Assert(f.FieldType is IRStructType);

			IRBasicAccessPath bap = GlobalInterpreterState.TProg.GetAccessPathForSingleField(f);
			TracingSystem.LogUseFromLocation(this.Address, bap, this.LocationType);

			return CompositeValue.ReadCompositeFrom(bap, this.m_objcontents, this.LocationType);
		}

		internal void StoreCompositeToField(CompositeValueEntry[] cv, IRMemberField f)
		{
			Debug.Assert(f.FieldType is IRStructType);

			IRBasicAccessPath bap = GlobalInterpreterState.TProg.GetAccessPathForSingleField(f);
			TracingSystem.LogOldComposite(this.Address, bap, CompositeValue.ReadCompositeFrom(bap, this.m_objcontents, this.LocationType));
			TracingSystem.LogModToLocation(this.Address, bap, this.LocationType);

			CompositeValue.StoreCompositeTo(cv, bap, this.m_objcontents, this.LocationType);
		}

		//////////////////////////////////////////////////////
		//Overidden accesses

		internal override IEnumerable<int> EnumerateTrgtAddresses()
		{
			CompositeValueEntry[] cvee = this.m_objcontents;
			for(int j = 0; j < cvee.Length; ++j)
			{
				if(cvee[j].Offset.PathLast.FieldType is IRPtrType)
				{
					PtrValue cpv = cvee[j].Value.ConvertToPassablePtrValue();
					yield return cpv.TargetAddress;
				}
			}
		}

		internal override PtrValue ReadPassablePtrValueFrom(ReferenceValue rf)
		{
			TracingSystem.LogUseFromLocation(this.Address, rf.TargetOffset, this.LocationType);

			return CompositeValue.ReadPassablePtrValueFrom(rf.TargetOffset, this.m_objcontents);
		}

		internal override void StorePassablePtrValTo(PtrValue pv, ReferenceValue rf)
		{
			TracingSystem.LogOldPointer(this.Address, rf.TargetOffset, CompositeValue.ReadPassablePtrValueFrom(rf.TargetOffset, this.m_objcontents));
			TracingSystem.LogModToLocation(this.Address, rf.TargetOffset, this.LocationType);

			CompositeValue.StorePassablePtrValTo(pv, rf.TargetOffset, this.m_objcontents);
		}

		internal override NumericValue ReadPassableNumericFrom(ReferenceValue rf)
		{
			TracingSystem.LogUseFromLocation(this.Address, rf.TargetOffset, this.LocationType);

			return CompositeValue.ReadPassableNumericFrom(rf.TargetOffset, this.m_objcontents);
		}

		internal override void StorePassableNumericValTo(NumericValue nv, ReferenceValue rf)
		{
			TracingSystem.LogOldNumeric(this.Address, rf.TargetOffset, CompositeValue.ReadPassableNumericFrom(rf.TargetOffset, this.m_objcontents));
			TracingSystem.LogModToLocation(this.Address, rf.TargetOffset, this.LocationType);

			CompositeValue.StorePassableNumericValTo(nv, rf.TargetOffset, this.m_objcontents);
		}

		internal override CompositeValueEntry[] ReadCompositeFrom(ReferenceValue rf)
		{
			Debug.Assert(!rf.TargetOffset.IsEmptyAccessPath);

			TracingSystem.LogUseFromLocation(this.Address, rf.TargetOffset, this.LocationType);

			return CompositeValue.ReadCompositeFrom(rf.TargetOffset, this.m_objcontents, this.LocationType);
		}

		internal override void StoreCompositeTo(CompositeValueEntry[] cv, ReferenceValue rf)
		{
			Debug.Assert(!rf.TargetOffset.IsEmptyAccessPath);

			TracingSystem.LogOldComposite(this.Address, rf.TargetOffset, CompositeValue.ReadCompositeFrom(rf.TargetOffset, this.m_objcontents, this.LocationType));
			TracingSystem.LogModToLocation(this.Address, rf.TargetOffset, this.LocationType);

			CompositeValue.StoreCompositeTo(cv, rf.TargetOffset, this.m_objcontents, this.LocationType);
		}
	}
}
